/*
 * process.hpp
 *
 *  Created on: Jul 26, 2010
 *      Author: sdaniele
 */

#ifndef PROCESS_HPP_
#define PROCESS_HPP_


#include <boost/utility.hpp>
#include "boost/concept/assert.hpp"
#include "boost/concept/requires.hpp"

namespace syscpar{namespace test{

struct action_helper
{
	template <class G> void operator()(G g) const
	{
		g.operator()();
	}
};


/*
 * Class process is both a sc_spawnable and a action.
 *
 * Defines 'operator()' to be spawned via sysc. & to be invoked as an action
 * process's action is to perform its actions (available via Actions template parameter) and then possibly re-spawn itself a few times
 *
 * Self re-spawning is controlled via compile-time recursion, specifically via 2 template arguments:
 *
 * 	1. arg
 * 		initial branch factor, that is how many instances are to be re-spawned in the 1st iteration
 *
 *  2. backoff_func
 *  	a closure that defines how 'arg' is changing over that time; finally 'arg' must drop to 0 to avoid infinite recursion
 *
 *  Note: a process may have an action to spawn another process, this is independent of self re-spawning
 */
template <class Actions
		, bool is_thread = false
		, class backoff_func = once<mpl::placeholders::_1>
		, int arg=0
		>
struct process
{
	BOOST_MPL_ASSERT_RELATION(arg,>=,0);

public:

	/*
	 * Invokes actions in Actions (must be an MPL Forward Sequence)
	 * Then proceeds to re-spawn
	 */
	void operator()() const
	{
		report();
		mpl::for_each<Actions>(action_helper());
		spawn();
	}


	/*
	 * Respawns 'arg' times, 'arg' is passed modified via compile-time recursion to spawned processes
	 *
	 */
private:
	inline void spawn() const
	{

		process<
				Actions
			  , is_thread
			  , backoff_func
			  , mpl::apply<
						backoff_func
					  , mpl::int_<arg>
					>::type::value
				> p;

		sc_spawn_options opt;
		if(!is_thread)
			opt.spawn_method();
		for(int i=0; i<arg; i++)
			sc_spawn(p,"",&opt);
	}


	void report() const
	{
		if(is_verbose())
			std::cout << "process reporting " << mpl::size< Actions >::value << " action(s) to perform, respawn " << arg << " times\n";
		else
			std::cout << "process reporting\n";
	}

};


}}


#endif /* PROCESS_HPP_ */
